from copy import copy

from mythril.analysis.module.base import DetectionModule, EntryPoint
from mythril.analysis.report import Issue
from mythril.laser.ethereum.state.constraints import Constraints
from mythril.laser.smt import symbol_factory, UGT, BitVec, Or
from mythril.laser.ethereum.state.global_state import GlobalState
from mythril.laser.ethereum.state.annotation import StateAnnotation
from mythril.analysis import solver
from mythril.exceptions import UnsatError
from typing import List, cast, Optional
from copy import copy
from mythril.analysis.module.module_helpers import is_prehook
from mythril.laser.ethereum.state.global_state import GlobalState
from mythril.laser.ethereum.state.annotation import StateAnnotation

from mythril.analysis.swc_data import DOS_WITH_BLOCK_GAS_LIMIT

from typing import cast, List


class GasDosAnnotation(StateAnnotation):
    

    def __init__(self, call_state: GlobalState) -> None:
        self.call_state = call_state
        self.jumptargets = []  # type: List[GlobalState]
        self.loopStart=0
        self.loopEnd=0
        self.count=0
        

    def __copy__(self):
        new_annotation = GasDosAnnotation(
            self.call_state
        )
        new_annotation.jumptargets = self.jumptargets[:]
        new_annotation.loopStart=self.loopStart
        new_annotation.loopEnd=self.loopEnd
        new_annotation.count=self.count
        return new_annotation

class GasDos(DetectionModule):
    """This module checks for GasDos ."""

    name = "GasDos"
    swc_id = DOS_WITH_BLOCK_GAS_LIMIT
    description = "Check for GasDos"
    entry_point = EntryPoint.CALLBACK
    pre_hooks = ["JUMP","JUMPI" ,"SSTORE"]

    def _execute(self, state: GlobalState) -> None:
        #print("Aaaaaaaaaaaaaaaaaa")
        if state.get_current_instruction()["address"] in self.cache:
            return
        issues = self._analyze_state(state)
        for issue in issues:
            self.cache.add(issue.address)
        self.issues.extend(issues)

    @staticmethod
    def _analyze_state(state: GlobalState):         
        """
        :param state: the current state
        :return: returns the issues for that corresponding state
        """
        issues = []

        instruction = state.get_current_instruction()["opcode"]
        state_address=state.get_current_instruction()["address"]

        annotations = cast(
            List[GasDosAnnotation],
            list(state.get_annotations(GasDosAnnotation)),
        )
        if len(annotations) == 0:
            state.annotate(GasDosAnnotation(state))
            annotations = cast(
                List[GasDosAnnotation],
                list(state.get_annotations(GasDosAnnotation)),
            )
        

        if state.get_current_instruction()["opcode"] in ["JUMP","JUMPI"]:


            target=state.mstate.stack[-1]

            for annotation in annotations:

                jumptarget=annotation.jumptargets
                if target in jumptarget:
                    annotation.count+=1
                else:
                    jumptarget.append(target)
                
                if annotation.count>2:
                    annotation.loopStart=state_address
                    annotation.loopEnd=target

            
        else:  # SSTORE

            for annotation in annotations:
                if annotation.loopStart and state_address<annotation.loopEnd and state_address>annotation.loopStart :
                
                    constraints = copy(state.world_state.constraints)

                    try:
                        transaction_sequence = solver.get_transaction_sequence(
                            state, constraints
                        )
                    except UnsatError:
                        continue
                    description_tail = (
                        "GasDosaaaaaaaaaaaaaaaaaaaaaaaaa "
                        
                    )
                    
                    issue = Issue(
                        contract=state.environment.active_account.contract_name,
                        function_name=state.environment.active_function_name,
                        address=state.get_current_instruction()["address"],
                        swc_id=DOS_WITH_BLOCK_GAS_LIMIT,
                        bytecode=state.environment.code.bytecode,
                        title="GasDosaaaaaaaaaaaaaaaaaaaaaaaaa",
                        severity="Low",
                        description_head="GasDosaaaaaaaaaaaaaaaaaaaaaaaaa",
                        description_tail=description_tail,
                        gas_used=(state.mstate.min_gas_used, state.mstate.max_gas_used),
                        transaction_sequence=transaction_sequence,
                    )

                    issues.append(issue)
                    

        return issues

detector = GasDos()